#include "KeyboardHandle.h"

// 引用Windows头文件
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif // WIN32_LEAN_AND_MEAN
#include <windows.h>

#include <vector>
#include <algorithm>
#include "MouseHandle.h"

// 定义全局变量
static HHOOK hook_header{nullptr};
static std::vector<PFN_KeyboardProcess> gKeyboardCallbacks{};
static std::array<bool,0x200> gKeyStateMapping{};

static void KeyboardDataConver(KeyBoardData &target, const KBDLLHOOKSTRUCT &data, WPARAM w)
{
    target.wParam = w;
    target.vkCode = data.vkCode;
    target.scanCode = data.scanCode;
    target.flags = data.flags;
    target.time = data.time;
    target.dwExtraInfo = data.dwExtraInfo;
}

static void UpdateKeyState(const KBDLLHOOKSTRUCT &data, const WPARAM w)
{
    gKeyStateMapping[data.vkCode] = (WM_KEYDOWN == w);
}

static LRESULT KeyboardHookProcess(int code, WPARAM w, LPARAM l)
{
    const KBDLLHOOKSTRUCT var = *reinterpret_cast<KBDLLHOOKSTRUCT *>(l);
    KeyBoardData localKeyData{};
    KeyboardDataConver(localKeyData, var, w);

    UpdateKeyState(var,w);

    for (auto fn : gKeyboardCallbacks)
    {
        fn(&localKeyData);
    }

    return CallNextHookEx(nullptr, code, w, l);
}

Keyboard &Keyboard::GetInstance()
{
    static Keyboard ins{};
    return ins;
}

void Keyboard::AddProc(PFN_KeyboardProcess fn)
{
    const auto localFinded = std::find(gKeyboardCallbacks.begin(), gKeyboardCallbacks.end(), fn);
    if (localFinded != gKeyboardCallbacks.end())
    {
        return;
    }

    gKeyboardCallbacks.push_back(fn);
}

void Keyboard::DelProc(PFN_KeyboardProcess fn)
{
    auto item = std::remove(gKeyboardCallbacks.begin(), gKeyboardCallbacks.end(), fn);
    (void)(item);
}

bool Keyboard::IsKeyDown(const char key)
{
    if (key < gKeyStateMapping.size())
    {
        return gKeyStateMapping[key];
    }
    
    return false;
}

Keyboard::Keyboard()
{
    hook_header = SetWindowsHookExW(WH_KEYBOARD_LL, KeyboardHookProcess, nullptr, 0u);
}

Keyboard::~Keyboard()
{
    if (nullptr != hook_header)
    {
        UnhookWindowsHookEx(hook_header);
    }
}